package validator

import (
	"regexp"
	"strconv"
	"strings"
	"unicode"

	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
)

// ValidateRule 验证规则
type ValidateRule struct {
	RegisterFunc    validator.RegisterTranslationsFunc
	TranslationFunc validator.TranslationFunc
	ValidateFunc    validator.Func
}

var commonRules = map[string]ValidateRule{
	"mobile": {
		RegisterFunc: func(ut ut.Translator) error {
			return ut.Add("mobile", "请检查{0}是否正确", true)
		},
		TranslationFunc: func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T("mobile", fe.Field())
			return t
		},
		ValidateFunc: func(fl validator.FieldLevel) bool {
			mobile, ok := fl.Field().Interface().(string)
			if ok {
				if mobile == "" {
					return true
				}
				return ValidMobile(mobile)
			}

			return false
		},
	},
	"id_card": {
		RegisterFunc: func(ut ut.Translator) error {
			return ut.Add("id_card", "请检查{0}是否正确", true)
		},
		TranslationFunc: func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T("id_card", fe.Field())
			return t
		},
		ValidateFunc: func(fl validator.FieldLevel) bool {
			idCard, ok := fl.Field().Interface().(string)
			if ok {
				if idCard == "" {
					return true
				}
				return ValidIDCard(idCard)
			}
			return false
		},
	},
	"email": {
		RegisterFunc: func(ut ut.Translator) error {
			return ut.Add("email", "请检查{0}是否正确", true)
		},
		TranslationFunc: func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T("email", fe.Field())
			return t
		},
		ValidateFunc: func(fl validator.FieldLevel) bool {
			email, ok := fl.Field().Interface().(string)
			if ok {
				if email == "" {
					return true
				}

				return ValidEmail(email)
			}
			return false
		},
	},
	"user_name": {
		RegisterFunc: func(ut ut.Translator) error {
			return ut.Add("user_name", "{0}只允许中文、字母、数字、下划线", true)
		},
		TranslationFunc: func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T("user_name", fe.Field())
			return t
		},
		ValidateFunc: func(fl validator.FieldLevel) bool {
			userName, ok := fl.Field().Interface().(string)
			if ok {
				if userName == "" {
					return true
				}
				return ValidUserName(userName)
			}
			return false
		},
	},
	"password_middle_complex": {
		RegisterFunc: func(ut ut.Translator) error {
			return ut.Add("password_middle_complex", "密码至少包含数字和字母且在6-20位之间", true)
		},
		TranslationFunc: func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T("password_middle_complex", fe.Field())
			return t
		},
		ValidateFunc: func(fl validator.FieldLevel) bool {
			val, ok := fl.Field().Interface().(string)
			if ok {
				if val == "" {
					return true
				}
				return ValidPasswordMiddleComplex(val)
			}
			return false
		},
	},
}

// ValidMobile 校验手机号
func ValidMobile(mobile string) bool {
	res := regexp.MustCompile("^1[3456789]\\d{9}$")
	return res.MatchString(mobile)
}

// ValidUserName 校验用户名
func ValidUserName(userName string) bool {
	res := regexp.MustCompile("^[\u4e00-\u9fa5a-zA-Z0-9_]{1,50}$")
	return res.MatchString(userName)
}

// ValidEmail 校验邮箱
func ValidEmail(email string) bool {
	res := regexp.MustCompile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")
	return res.MatchString(email)
}

// ValidPasswordMiddleComplex 检验密码
func ValidPasswordMiddleComplex(pass string) bool {
	var (
		hasLetter = false
		hasNumber = false
	)

	if len(pass) < 6 || len(pass) > 20 {
		return false
	}

	for _, v := range pass {
		if unicode.IsNumber(v) {
			hasNumber = true
		}

		if unicode.IsLetter(v) {
			hasLetter = true
		}
	}

	if hasNumber && hasLetter {
		return true
	}

	return false
}

// ValidIDCard 检查身份证号
func ValidIDCard(idNumber string) bool {
	reg, _ := regexp.Compile("^\\d{17}[\\dX]$")
	if !reg.MatchString(idNumber) {
		return false
	}

	weight := [17]int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
	check := [11]interface{}{1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2}
	sum := 0
	for i := 0; i < 17; i++ {
		itemInt, err := strconv.Atoi(idNumber[i : i+1])
		if err != nil {
			return false
		}
		sum += weight[i] * itemInt
	}

	mod := sum % 11
	if mod == 2 {
		if strings.ToUpper(idNumber[17:18]) != check[sum%11].(string) {
			return false
		}
	} else {
		itemInt, err := strconv.Atoi(idNumber[17:18])
		if err != nil {
			return false
		}

		if check[sum%11].(int) != itemInt {
			return false
		}
	}

	return true

}
